use std::{collections::HashMap, fmt::Debug, sync::Arc};

use anyhow::Error;
use log::error;
use serde_json::Value;

pub struct ToolCollection<V>
where
    V: ToolSigEncoder,
{
    pub tools: Arc<HashMap<String, Arc<dyn Tool>>>,
    pub sig: Arc<V::BatchEncodedType>,
}

impl<V> Clone for ToolCollection<V>
where
    V: ToolSigEncoder,
{
    fn clone(&self) -> Self {
        Self {
            tools: self.tools.clone(),
            sig: self.sig.clone(),
        }
    }
    fn clone_from(&mut self, source: &Self) {
        self.tools.clone_from(&source.tools);
        self.sig.clone_from(&source.sig);
    }
}

pub struct ToolCollectionBuilder {
    tools: Vec<Arc<dyn Tool>>,
}

impl ToolCollectionBuilder {
    pub fn new() -> Self {
        Self { tools: vec![] }
    }
    pub fn add<T>(mut self) -> Self
    where
        T: Tool + 'static,
    {
        self.tools.push(Arc::from(T::new()));
        self
    }
    pub fn build<V>(self) -> ToolCollection<V>
    where
        V: ToolSigEncoder,
    {
        let sig = V::toolsig_encode_batch(&self.tools);
        let tools = self.tools.into_iter().map(|v| {
            let name = v.name().to_owned();
            (name, v)
        });
        let tools = HashMap::from_iter(tools);
        ToolCollection {
            tools: Arc::new(tools),
            sig: Arc::new(sig),
        }
    }
}

/// 工具签名
pub struct ToolSig {
    pub name: String,
    pub description: String,
    pub parameters: Vec<ToolSigParameter>,
}

pub struct ToolSigParameter {
    pub name: String,
    pub description: String,
    pub required: bool,
    pub parameter_type: ToolSigParameterType,
}

pub enum ToolSigParameterType {
    String,
    Int,
    Float,
    Bool,
    Null,
}

impl ToolSig {
    pub fn new(name: String, description: String) -> Self {
        Self {
            name,
            description,
            parameters: vec![],
        }
    }
    pub fn add_parameter(mut self, parameter: ToolSigParameter) -> Self {
        self.parameters.push(parameter);
        self
    }
}

impl ToolSigParameter {
    pub fn new(
        name: String,
        description: String,
        required: bool,
        parameter_type: ToolSigParameterType,
    ) -> Self {
        Self {
            name,
            description,
            required,
            parameter_type,
        }
    }
}
impl ToolSigParameterType {
    pub fn as_str(&self) -> &str {
        match self {
            ToolSigParameterType::String => "string",
            ToolSigParameterType::Bool => "bool",
            ToolSigParameterType::Float => "float",
            ToolSigParameterType::Int => "int",
            ToolSigParameterType::Null => "null",
        }
    }
}

#[derive(Debug, Clone)]
/// 工具调用时的参数
pub struct ToolParm {
    name: String,
    vals: HashMap<String, ToolParmVal>,
}

#[derive(Debug, Clone)]
pub enum ToolParmVal {
    String(String),
    Int(i32),
    Float(f32),
    Bool(bool),
    Null,
}

impl ToolParm {
    pub fn new(name: String, vals: HashMap<String, ToolParmVal>) -> Self {
        Self { name, vals }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn vals(&self) -> &HashMap<String, ToolParmVal> {
        &self.vals
    }
    pub fn get(&self, key: &str) -> Option<&ToolParmVal> {
        self.vals.get(key)
    }
}

impl ToolParmVal {
    pub fn from_value(val: Value) -> Self {
        match val {
            Value::Null => Self::Null,
            Value::Bool(b) => Self::Bool(b),
            Value::Number(_) => {
                if let Some(i) = val.as_i64() {
                    Self::Int(i as i32)
                } else if let Some(f) = val.as_f64() {
                    Self::Float(f as f32)
                } else {
                    Self::Null
                }
            }
            Value::String(s) => Self::String(s),
            Value::Array(_) => Self::Null,
            Value::Object(_) => Self::Null,
        }
    }
}
pub struct ToolOutput {
    pub content: Option<String>,
    pub is_ok: bool,
}

impl ToolOutput {
    pub fn is_ok(&self) -> bool {
        self.is_ok
    }
    pub fn as_str(&self) -> Option<&str> {
        match &self.content {
            Some(content) => Some(&content),
            None => None,
        }
    }
}
pub trait ToolSigBatchEncodedType: Sized {}
pub trait ToolSigEncoder: Clone {
    type BatchEncodedType: ToolSigBatchEncodedType;
    fn toolsig_encode_batch(tools: &Vec<Arc<dyn Tool>>) -> Self::BatchEncodedType;
}

impl ToolSigBatchEncodedType for String {}
#[async_trait::async_trait]
pub trait Tool {
    fn name(&self) -> &'static str;
    fn new() -> Self
    where
        Self: Sized;
    fn sig(&self) -> ToolSig;
    async fn run(&self, parm: ToolParm) -> anyhow::Result<ToolOutput>;
}

impl<V> ToolCollection<V>
where
    V: ToolSigEncoder,
{
    pub async fn run(&self, name: &str, parm: ToolParm) -> anyhow::Result<ToolOutput> {
        if let Some(tool) = self.tools.get(name) {
            return tool.run(parm).await;
        } else {
            error!("Required function {} not found.", name);
            return Err(Error::msg("Required function not found."));
        }
    }
}
